package com.spooky.sync.persistence.db;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;
import com.spooky.sync.persistence.entity.BaseEntity;

//TODO add try catch for all sql queries
public abstract class ExtendedDAOBean<T extends BaseEntity> implements CreateSqlSyntax {
	protected static final String COL_ID = "_id";
	private static final String TAG = "ExtendedDAOBean";
	private UpdateSql mUpdateSql = null;
	protected SQLiteDatabase mSQLiteDatabase = null;
	protected Map<Integer, String> mSyntaxUpdate = new LinkedHashMap<Integer, String>();

	public ExtendedDAOBean(Context context) {
		mUpdateSql = UpdateSql.getInstance(context);
		mSQLiteDatabase = mUpdateSql.getWritableDatabase();
	}

	protected ExtendedDAOBean() {
	}

	protected abstract void assign(BaseEntity entity, Cursor result) throws Exception;

	protected abstract int assign(BaseEntity entity, SQLiteStatement statement) throws SQLException;

	protected List<BaseEntity> findMultiple(PreparedStatement statement, Object... params) {
		return new LinkedList<BaseEntity>();// TODO
	}

	protected List<BaseEntity> findMultiple(Statement statement) {
		return new LinkedList<BaseEntity>();
	}

	protected BaseEntity findEntity(PreparedStatement statement, Object... params) {
		return null;
	}

	protected T findEntity(Long id) {
		return assemble(mSQLiteDatabase.query(getTableName(), getCollumns(), String.format("%S = %d", COL_ID, id), null, null, null, COL_ID, "1"));
	}

	protected T create(BaseEntity entity) {
		open();
		SQLiteStatement insertStatement = getInsertStatement();
		try {
			assign(entity, insertStatement);
			long key = insertStatement.executeInsert();
			entity = setEntity(entity, key);
		} catch (SQLiteConstraintException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			insertStatement.clearBindings();
			insertStatement.close();
		}
		return (T) entity;
	}

	protected List<T> listEntities() {
		return assembleList(mSQLiteDatabase.query(getTableName(), getCollumns(), null, null, null, null, COL_ID));
	}

	protected T updateEntity(T entity) {
		return entity;
	}

	public void onDestroy() {
		mSQLiteDatabase.close();
	}

	protected void deleteEntity(T entity) {
		mSQLiteDatabase.delete(getTableName(), COL_ID + " = ?", new String[] { String.valueOf(entity.getId()) });
	}

	protected abstract T getEntity();

	protected SQLiteStatement getSelectStatement() {
		// TODO fix
		return mSQLiteDatabase.compileStatement(String.format("SELECT * FROM ?", getTableName()));
	}

	protected void close() {
		mSQLiteDatabase.close();
	}

	private SQLiteStatement getInsertStatement() {
		StringBuffer query = new StringBuffer();
		StringBuffer values = new StringBuffer();
		values.append("VALUES(");
		query.append("INSERT INTO ");
		query.append(getTableName());
		query.append(" (");
		for (String collum : getCollumns()) {
			if (!collum.equals("_id")) {
				query.append(collum);
				query.append(",");
				values.append("?,");
			}
		}
		query.delete(query.length() - 1, query.length());
		values.delete(values.length() - 1, values.length());
		query.append(") ");
		query.append(values);
		query.append(");");
		Log.d(TAG, query.toString());
		return mSQLiteDatabase.compileStatement(query.toString());
	}

	private T assemble(Cursor result) {
		BaseEntity entity = null;
		if (result.moveToFirst()) {
			try {
				entity = newEntity(result.getLong(result.getColumnIndexOrThrow(COL_ID)));
				assign(entity, result);
			} catch (Exception e) {
				entity = new BaseEntity();
			}
		}
		result.close();
		return (T) entity;
	}

	private List<T> assembleList(Cursor result) {
		List<T> list = new LinkedList<T>();
		BaseEntity entity = null;
		if (result.moveToFirst()) {
			do {
				try {
					entity = newEntity(result.getLong(result.getColumnIndexOrThrow(COL_ID)));
					assign(entity, result);
					list.add((T) entity);
				} catch (Exception e) {
				}
			} while (result.moveToNext());
		}
		result.close();
		return list;
	}

	private T newEntity(Long id) {
		BaseEntity entity = getEntity();
		try {
			Field field = BaseEntity.class.getDeclaredField(BaseEntity.ID);
			field.setAccessible(true);
			field.set(entity, id);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return (T) entity;
	}

	private BaseEntity setEntity(BaseEntity entity, Long id) {
		try {
			Class<BaseEntity> c = BaseEntity.class;
			Field field = c.getDeclaredField(BaseEntity.ID);
			field.setAccessible(true);
			field.set(entity, id);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return entity;
	}

	private void open() {
		if (!mSQLiteDatabase.isOpen()) {
			mSQLiteDatabase = mUpdateSql.getWritableDatabase();
		}
	}
}
